﻿using Awesomium.Core;
using Awesomium.Windows.Controls;
using browser4gp.core;
using browser4gp.core.Helpers;
using browser4gp.core.Plugin;
using browser4gp.core.Style;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using wpf4gp.ViewModels;

namespace browser4gp.ViewModels
{

    /// <summary>
    /// View model per il browser
    /// </summary>
    public class BrowserViewModel : BaseWindowViewModel, IDisposable
    {

        #region Costruttore

        /// <summary>
        /// Costruttore
        /// </summary>
        /// <param name="uri">Uri della pagina</param>
        public BrowserViewModel(string uri)
        {
            WebCore.Initialize(new WebConfig
            {
                LogLevel = LogLevel.Verbose,
                UserAgent = "WinApp",
                RemoteDebuggingHost = "127.0.0.1",
                RemoteDebuggingPort = 9222,
                HomeURL = new Uri(uri)
            });
            // Carico gli stili
            LoadStyles();
            // Carico i plugin
            LoadPlugins();
            // Visualizzo i dati
            Source = new Uri(uri);
        }

        #endregion


        #region Gestione MEF

        /// <summary>
        /// Caricamento dei plugin
        /// </summary>
        private void LoadPlugins()
        {
            var catalog = new AggregateCatalog();
            foreach (string subdir in Directory.GetDirectories(EnvironmentHelper.PluginsDirectory))
            {
                catalog.Catalogs.Add(new DirectoryCatalog(subdir, "*Plugin.dll"));   
            }
            MefContaier = new CompositionContainer(catalog);
            MefContaier.ComposeExportedValue("MainWindowStyle", WindowStyle);
            MefContaier.ComposeParts(this);
        }



        private CompositionContainer _mefContainer;

        /// <summary>
        /// Gestore di MEF
        /// </summary>
        public CompositionContainer MefContaier
        {
            get
            {
                return _mefContainer;
            }
            private set
            {
                if (_mefContainer != value)
                {
                    _mefContainer = value;
                    RaisePropertyChanged(() => MefContaier);
                }
            }
        }


        #endregion


        #region Gestione JavaScript


        private Dictionary<uint, List<IBrowserPlugin>> AssociatedPlugins = new Dictionary<uint, List<IBrowserPlugin>>();

        /// <summary>
        /// Inizio caricamento frame
        /// </summary>
        /// <param name="e">Evento di inizio caricamento</param>
        public void LoadingFrameStart(object sender, LoadingFrameEventArgs e)
        {
            IsLoading = true;
        }

        /// <summary>
        /// Fine caricamento frame
        /// </summary>
        /// <param name="e">Evento di fine caricamento</param>
        public void LoadingFrameComplete(object sender, FrameEventArgs e)
        {
            AssociatedPlugins = new Dictionary<uint, List<IBrowserPlugin>>();
            WebControl control = sender as WebControl;
            if (sender != null)
            {
                // Aggiungo eventi dei plugin
                foreach (Lazy<IBrowserPlugin, IBrowserPluginMetadata> plugin in Plugins)
                {
                    string javascriptObjectName = plugin.Metadata.JavascriptObjectName;
                    if (!string.IsNullOrEmpty(javascriptObjectName))
                    {
                        using (JSObject javascriptObject = control.ExecuteJavascriptWithResult(javascriptObjectName))
                        {
                            if (javascriptObject != null)
                            {
                                uint remoteId = javascriptObject.RemoteId;
                                List<IBrowserPlugin> associatedPluginsList;
                                if (AssociatedPlugins.ContainsKey(remoteId))
                                {
                                    associatedPluginsList = AssociatedPlugins[remoteId];
                                }
                                else
                                {
                                    associatedPluginsList = new List<IBrowserPlugin>();
                                }
                                associatedPluginsList.Add(plugin.Value);
                                AssociatedPlugins[remoteId] = associatedPluginsList;
                                foreach (string methodName in javascriptObject.GetMethodNames())
                                {
                                    javascriptObject.Bind(methodName, true, JSHandler);
                                }
                            }
                        }
                    }
                }
                // Aggiungo browser4gp (questa app)
                using (JSObject javascriptObject = control.ExecuteJavascriptWithResult("browser4gp"))
                {
                    if (javascriptObject != null)
                    {
                        foreach (string methodName in javascriptObject.GetMethodNames())
                        {
                            javascriptObject.Bind(methodName, true, BrowserJSHandler);
                        }
                    }
                }
            }
            IsLoading = false;
        }

        /// <summary>
        /// Caricamento frame fallito
        /// </summary>
        /// <param name="e">Evento di errore caricamento</param>
        public void LoadingFrameFailed(object sender, LoadingFrameFailedEventArgs e)
        {
            IsLoading = false;
            if (e.HasErrors)
            {
                throw new ApplicationException(string.Format("ERR: {0} - {1}", e.ErrorCode, e.ErrorDescription));
            }
        }



        /// <summary>
        /// Intercetta gli eventi javascript
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void JSHandler(object sender, JavascriptMethodEventArgs args)
        {
            uint remoteId = args.RemoteObjectId;
            if (AssociatedPlugins.ContainsKey(remoteId))
            {
                foreach (IBrowserPlugin plugin in AssociatedPlugins[remoteId])
                {
                    string method = args.MethodName;
                    List<string> arguments = new List<string>();
                    foreach (var arg in args.Arguments)
                    {
                        if (arg.IsNull ||
                            arg.IsUndefined)
                        {
                            arguments.Add(string.Empty);
                        }
                        else
                        {
                            arguments.Add(arg.ToString());
                        }
                    }
                    args.Result = plugin.RunFromJavascript(method, arguments);
                }
            }
        }



        /// <summary>
        /// Intercetta gli eventi javascript relativi al javascript browser4gp (questo)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void BrowserJSHandler(object sender, JavascriptMethodEventArgs args)
        {
            switch (args.MethodName)
            {
                case "showStatusBar":
                    StatusBarVisibility = Visibility.Visible;
                    break;
                case "hideStatusBar":
                    StatusBarVisibility = Visibility.Collapsed;
                    break;
                case "setStatusBarMessage":
                    if (args.Arguments.Length == 1)
                    {
                        StatusBarMessage = args.Arguments[0];
                    }
                    else
                    {
                        StatusBarMessage = null;
                    }
                    break;
                case "exit":
                    DialogResult = true;
                    break;
                case "setCulture":
                    try
                    {
                        CultureInfo newCulture = new CultureInfo(args.Arguments[0]);
                        Thread.CurrentThread.CurrentCulture = newCulture;
                        Thread.CurrentThread.CurrentUICulture = newCulture;
                        Application.Current.MainWindow.UpdateLayout();
                    }
                    catch { }
                    break;
                default:
                    break;
            }
        }

        #endregion


        #region Plugins
        

        [ImportMany]
        private IEnumerable<Lazy<IBrowserPlugin, IBrowserPluginMetadata>> _plugins;


        /// <summary>
        /// Elenco dei plugin installati
        /// </summary>
        public IEnumerable<Lazy<IBrowserPlugin, IBrowserPluginMetadata>> Plugins
        {
            get
            {
                return _plugins;
            }
            set
            {
                if (_plugins != value)
                {
                    _plugins = value;
                    RaisePropertyChanged(() => Plugins);
                }
            }
        }


        /// <summary>
        /// Elementi visibili nella status bar
        /// </summary>
        public List<UIElement> PluginsStatusBarElements
        {
            get
            {
                return Plugins.Where(x => x.Value.StatusBarElement != null)
                              .Select(x => x.Value.StatusBarElement)
                              .ToList();
            }
        }

        #endregion


        #region Source


        private Uri _source;

        /// <summary>
        /// Source della pagina
        /// </summary>
        public Uri Source
        {
            get
            {
                return _source;
            }
            set
            {
                if (_source != value)
                {
                    _source = value;
                    RaisePropertyChanged(() => Source);
                }
            }
        }


        #endregion


        #region Dispose

        /// <summary>
        /// Distruttore
        /// </summary>
        public void Dispose()
        {
            WebCore.Shutdown();
        }


        #endregion


        #region Stili


        private void LoadStyles()
        {
            // Leggo i settings
            string settingPath = Path.Combine(EnvironmentHelper.ApplicationDirectory, "Style");
            JsonSerializer deserializer = new JsonSerializer();
            deserializer.Converters.Add(new JsonDeserializerHelper());
            try
            {
                if (Directory.Exists(settingPath))
                {
                    string fileName = Path.Combine(settingPath, "windowStyle.json");
                    if (File.Exists(fileName))
                    {
                        JsonTextReader reader = new JsonTextReader(new StreamReader(fileName));
                        WindowStyle = deserializer.Deserialize<MainWindowStyle>(reader);
                    }
                }
            }
            catch
            {

            }
            if (WindowStyle == null)
            {
                WindowStyle = new MainWindowStyle();
            }
        }


        private MainWindowStyle _windowStyle;

        /// <summary>
        /// Stile della finestra
        /// </summary>
        public MainWindowStyle WindowStyle
        {
            get
            {
                return _windowStyle;
            }
            set
            {
                if (_windowStyle != value)
                {
                    _windowStyle = value;
                    RaisePropertyChanged(() => WindowStyle);
                }
            }
        }



        #endregion
        

        #region Titolo


        private string _title;

        /// <summary>
        /// Titolo
        /// </summary>
        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                if (_title != value)
                {
                    _title = value;
                    RaisePropertyChanged(() => Title);
                }
            }
        }


        #endregion


        #region StatusBar


        #region StatusBar Message

        private string _statusBarMessage;

        /// <summary>
        /// Testo status bar
        /// </summary>
        public string StatusBarMessage
        {
            get
            {
                return _statusBarMessage;
            }
            set
            {
                if (_statusBarMessage != value)
                {
                    _statusBarMessage = value;
                    RaisePropertyChanged(() => StatusBarMessage);
                }
            }
        }


        #endregion


        #region Visibility


        private Visibility _statusBarVisibility = Visibility.Collapsed;

        /// <summary>
        /// Visibilità della status bar
        /// </summary>
        public Visibility StatusBarVisibility
        {
            get
            {
                return _statusBarVisibility;
            }
            set
            {
                if (_statusBarVisibility != value)
                {
                    _statusBarVisibility = value;
                    RaisePropertyChanged(() => StatusBarVisibility);
                }
            }
        }

        #endregion


        #endregion


    }
}
